package org.openclinic.controller;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;

import javax.servlet.http.HttpServletRequest;

import org.joda.time.LocalTime;
import org.joda.time.Minutes;
import org.openclinic.domain.Appointment;
import org.openclinic.domain.Attendant;
import org.openclinic.domain.Clinic;
import org.openclinic.domain.Customer;
import org.openclinic.domain.HealthProfessional;
import org.openclinic.domain.MedicalSpecialty;
import org.openclinic.domain.ScheduleHeader;
import org.openclinic.exception.AbstractExceptionHandler;
import org.openclinic.exception.DomainException;
import org.openclinic.exception.ErrorCodes;
import org.openclinic.service.AppointmentService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
public class AppointmentController extends AbstractExceptionHandler {

	private static final int APPOINTMENT_DURANTION_UNIT = 15;
	
	@Autowired
	private Clinic clinic;
	
	@Autowired
	private AppointmentService appointmentService;

	@ResponseBody
	@RequestMapping(value = "/appointment/cancel.action", method = RequestMethod.POST)
	public void cancel(@RequestParam long appointmentId) {
		appointmentService.cancel(appointmentId);
	}

	@ResponseBody
	@RequestMapping(value = "/appointment/load.action", method = RequestMethod.POST)
	public Appointment findByAppointmentId(@RequestParam long appointmentId) {
		return appointmentService.find(appointmentId);
	}
	
	@ResponseBody
	@RequestMapping(value = "/appointment/find.action", method = RequestMethod.POST)
	public Long find(@RequestParam long customerId, HttpServletRequest request) {
		HealthProfessional healthProfessional = (HealthProfessional)request.getSession().getAttribute("currentUser");
		Appointment appointment = appointmentService.find(healthProfessional.getUserId(), customerId);
		if (appointment != null) {
			return appointment.getAppointmentId();
		}
		return null;
	}
	
	@ResponseBody
	@RequestMapping(value = "/appointment/history.action", method = RequestMethod.POST)
	public List<Appointment> findByCustomer(@RequestParam long customerId) {
		return appointmentService.findByCustomer(customerId);
	}
	
	@ResponseBody
	@RequestMapping(value = "/appointment/save.action", method = RequestMethod.POST)
	public Appointment save(@RequestParam(required = false) Long id, @RequestParam boolean scheduling, @RequestParam long customerId,
						@RequestParam long specialtyId, @RequestParam long professionalId, @RequestParam String appointmentDate,
						HttpServletRequest request) {
		SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy HH:mm");
		Date startDate = null;
		if (scheduling) {
			try {
				startDate = sdf.parse(appointmentDate);
			} catch (ParseException e) {
				throw new DomainException("appointment", "appointmentDate", ErrorCodes.INVALID_VALUE + ".appointmentDate", appointmentDate);
			}
		} else {
			startDate = new Date();
		}
		
		Attendant attendant = (Attendant)request.getSession().getAttribute("currentUser");

		Customer customer = new Customer();
		customer.setUserId(customerId);
		
		MedicalSpecialty specialty = new MedicalSpecialty();
		specialty.setSpecialtyId(specialtyId);
		
		HealthProfessional professional = new HealthProfessional();
		professional.setUserId(professionalId);
		
		Appointment appointment = new Appointment();
		appointment.setAppointmentId(id);
		appointment.setFit(!scheduling);
		appointment.setMedicalSpecialty(specialty);
		appointment.setAttendant(attendant);
		appointment.setCustomer(customer);
		appointment.setHealthProfessional(professional);
		appointment.setStartDate(startDate);
		
		return appointmentService.save(appointment);
	}
	
	@ResponseBody
	@RequestMapping(value = "/schedule/headers.action", method = RequestMethod.POST)
	public List<ScheduleHeader> getHeaders(@RequestParam(required = false) String startDate) {
		return appointmentService.getScheduleHeaders(startDate);
	}
	
	@ResponseBody
	@RequestMapping(value = "/schedule/appointments.action", method = RequestMethod.POST)
	public int[][][] findAppointments(@RequestParam String startDate, @RequestParam long specialtyId, @RequestParam long professionalId) {
		LocalTime morningStartTime = new LocalTime(clinic.getMorningStartTime());
		LocalTime afternoonEndTime = new LocalTime(clinic.getAfternoonEndTime());
		boolean extendEndHour = afternoonEndTime.getMinuteOfHour() != 0;
		int totalBusinessHours = afternoonEndTime.getHourOfDay() - morningStartTime.getHourOfDay() + (extendEndHour ? 1 : 0);
		
		return appointmentService.find(startDate, specialtyId, professionalId, morningStartTime.getHourOfDay(), totalBusinessHours);
	}
	
	@ResponseBody
	@RequestMapping(value = "/schedule/timebreaks.action", method = RequestMethod.POST) 
	public int[][] getTimeBreakList() {
		LocalTime morningStartTime = new LocalTime(clinic.getMorningStartTime());
		LocalTime morningEndTime = new LocalTime(clinic.getMorningEndTime());
		LocalTime afternoonStartTime = new LocalTime(clinic.getAfternoonStartTime());
		LocalTime afternoonEndTime = new LocalTime(clinic.getAfternoonEndTime());
		boolean extendEndHour = afternoonEndTime.getMinuteOfHour() != 0;
		int totalBusinessHours = afternoonEndTime.getHourOfDay() - morningStartTime.getHourOfDay() + (extendEndHour ? 1 : 0);
		
		int[][] timebreak = new int[totalBusinessHours][4];
		
		int beforeStartBreak = Minutes.minutesBetween(new LocalTime(morningStartTime.getHourOfDay() + ":00"), morningStartTime).getMinutes();
		int lunchBreak = Minutes.minutesBetween(morningEndTime, afternoonStartTime).getMinutes();
		int afterEndBreak = Minutes.minutesBetween(afternoonEndTime, new LocalTime((afternoonEndTime.getHourOfDay() + (extendEndHour ? 1 : 0)) + ":00")).getMinutes();

		int units;
		if (beforeStartBreak > 0) {
			units = beforeStartBreak / APPOINTMENT_DURANTION_UNIT;
			for (int i = 0; i < units; i++) {
				timebreak[0][i] = 1;
			}
		}
		
		if (lunchBreak > 0) {
			int line = morningEndTime.getHourOfDay() - morningStartTime.getHourOfDay();
			int column = morningEndTime.getMinuteOfHour() / APPOINTMENT_DURANTION_UNIT;
			units = lunchBreak / APPOINTMENT_DURANTION_UNIT;
			for (int i = 0; i < units; i ++) {
				timebreak[line][column] = 1;
				if (column > 0 && column % 3 == 0) {
					column = 0;
					line++;
				} else {
					column++;
				}
			}
		}
		
		if (afterEndBreak > 0) {
			units = afternoonEndTime.getMinuteOfHour() / APPOINTMENT_DURANTION_UNIT;
			for (int i = units; i < 4; i++) {
				timebreak[totalBusinessHours - 1][i] = 1;
			}
		}
		
		return timebreak;
	}

}
